Repository navigation
feat: operator role by name prefix + init ops in HH loop - #18
Merged
Merged
Conversation
jcrvz
previously approved these changes
Jun 25, 2026
There was a problem hiding this comment.
Pull request overview
This PR extends CUSTOMHYS’ hyper-heuristic search space so population initialization schemes can be selected and mutated as part of the candidate solution genotype (initializer at head + perturbation tail), using name-prefix role classification (initialize_*).
Changes:
- Added
initialize_*operator functions and prefix-based role detection incustomhys/operators.py, plus a default initializer collection file. - Updated
Population.initialise_positions()to accept tuple-form schemes and forward kwargs to parameterized initializers. - Extended
Hyperheuristicto recognize initializer operators, preserve them at position 0 during SA mutations, and extract the initializer inevaluate_candidate_solution, with new test coverage.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
customhys/operators.py |
Adds initializer operators, role detection, and updates operator build/decode helpers. |
customhys/population.py |
Extends initialization API to accept tuple schemes and kwargs; adds scramble support for Sobol/Halton. |
customhys/hyperheuristic.py |
Adds initializer-aware candidate generation/mutations and head extraction in evaluation. |
customhys/collections/default_initializers.txt |
New default initializer operator collection. |
tests/test_initialization_operators.py |
New tests for role classification and initializer operator behavior. |
tests/test_hyperheuristic_initialization.py |
New tests for initializer-aware HH behavior, including SA loop integration. |
Comments suppressed due to low confidence (2)
customhys/hyperheuristic.py:461
- In
Swap, thetail_lenbranches (<=1,==2,>2) already cover every possible case, so the subsequentelif current_cardinality == 2/> 2branches are unreachable. Removing these dead branches will make the logic clearer and reduce maintenance risk.
elif current_cardinality == 2:
encoded_neighbour = np.copy(sol)[::-1]
elif current_cardinality > 2:
encoded_neighbour = np.copy(sol)
ind1, ind2 = np.random.choice(current_cardinality, 2, replace=False)
encoded_neighbour[ind1], encoded_neighbour[ind2] = encoded_neighbour[ind2], encoded_neighbour[ind1]
else:
raise HyperheuristicError("Swap cannot be applied! current_cardinality < 2")
customhys/operators.py:1586
process_operatorscurrently builds a string representation with two issues: (1)if len(parameters) >= 0:is always true, so theelsebranch is dead; (2) for string values it appends the bare parameter name and thekey='value'form, producing misleading/invalid-looking operator strings (e.g.distribution,distribution='uniform'). These strings are used for logging and as stable identifiers inMetaheuristic.apply_search_operator, so they should be deterministic and well-formed.
for op_tuple in simple_heuristics:
operator, parameters, selector = op_tuple[0], op_tuple[1], op_tuple[2]
# Store selectors
selectors.append(selector)
if len(parameters) >= 0:
sep = ","
str_parameters = []
for parameter, value in parameters.items():
# Check if a value is string
if isinstance(value, str):
str_parameters.append(parameter)
str_parameters.append(f"{parameter}='{value}'")
else:
str_parameters.append(f"{parameter}={value}")
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
J0NGS
marked this pull request as draft
June 27, 2026 17:38
J0NGS
marked this pull request as ready for review
June 27, 2026 17:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was added
This PR continues the work started in #14, where population initialization schemes were added to
Population.The goal now is to make these schemes part of the hyper-heuristic search process as well. In practice, the hyper-heuristic off CUSTOMHYS can now choose not only the sequence of perturbation operators, but also how the initial population should be created.
The implementation follows @jcrvz’s suggestion in issue #16: treating initializers as first-class operators within the heuristic space itself, while distinguishing them from perturbation operators through an explicit classification.
Main changes
operators.py— initializers as operatorsThe initialization schemes added in #14 are now also available as operator functions:
initialize_randominitialize_sobolinitialize_haltoninitialize_gridinitialize_lhsinitialize_betainitialize_normalinitialize_lognormalinitialize_exponentialinitialize_rayleighThese functions were added to
__all__, along with their respective aliases.A simple prefix-based role classification was also introduced through
_ROLE_PREFIXES:With this approach, the role of each operator is derived directly from its name. This avoids adding an extra field to the tuples and keeps role-related strings centralized.
It also makes future extensions straightforward. If new operator types are introduced later, such as
adapt_orselect_, they can be supported by adding a single new prefix to the dictionary.customhys/collections/default_initializers.txtA new collection file was added with the initialization operators in the framework’s standard 3-tuple format.
This file can be loaded by
Hyperheuristictogether with any perturbation operator collection.hyperheuristic.py— initializer support in the SA loopWhen the heuristic space contains operators with role
"initialize", the hyper-heuristic now treats position0of each candidate solution as the selected initializer.The remaining positions continue to represent the perturbation operator sequence.
The main changes include:
initialization_operators: a new property that lists the initialization operators available in the heuristic space, along with their indices.0is always filled with an initialization operator, while the rest of the solution is generated normally.Swap,Mirror,Roll,Shift,Remove, and similar ones were updated to operate only on the tail of the solution, preserving the initializer at the head.ChangeInitOperator: a new action that replaces the initialization operator at position0with another available initializer. This action is enabled only when at least two initializers exist in the space.evaluate_candidate_solution: the initialization operator is extracted from the head of the solution, converted intoinitial_scheme, and passed toMetaheuristic, which then forwards it toPopulation.initialise_positions().Metaheuristicstill receives only the tail 3-tuples, so the existing perturbation behavior remains unchanged.RemoveandRemoveManynow return the current solution unchanged when the cardinality is already at the minimum with initializers present, instead of raisingHyperheuristicError.Tests
Two new test files were added, with a total of 95 tests.
tests/test_initialization_operators.pyCovers:
_get_role_ROLE_PREFIXESprocess_operatorsinitialize_*functionsPopulation.initialise_positions()variantstests/test_hyperheuristic_initialization.pyCovers:
initialization_operatorspropertyChangeInitOperatoractionevaluate_candidate_solutionwith an initialization operatorbuild_operatorsSphere,Rastrigin, andAckley1frombenchmark_funcBackward compatibility
Heuristic spaces without initialization operators continue to work as before.
In these cases,
_get_rolereturns"perturb"by default, and all initializer-specific behavior remains disabled.